/*
 * $Id: PreferencesTableModel.java,v 1.9 2002/12/07 18:02:18 jppietsch Exp $
 * Copyright (C) 2002 Jan-Peter Pietsch
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package de.jppietsch.prefedit;

import de.jppietsch.error.Log;
import de.jppietsch.error.Precondition;
import de.jppietsch.util.RscBundle;

import javax.swing.table.AbstractTableModel;
import java.util.logging.Level;
import java.util.prefs.BackingStoreException;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;


/**
 * A table model to display a {@link Preferences} as a table where each entry is
 * a row. The keys are in column 0 and the values are in column 2.
 *
 * @author Jan-Peter Pietsch
 * @version $Revision: 1.9 $
 */
public class PreferencesTableModel extends AbstractTableModel
        implements PreferenceChangeListener {

    // constants

    /**
     * Description of the Field
     */
    public final static int VALUE_COLUMN = 1;

    /**
     * Description of the Field
     */
    private final static int KEY_COLUMN = 0;

    /**
     * Description of the Field
     */
    private final static String[] EMPTY_KEYS = {};

    // fields

    /**
     * Description of the Field
     */
    private Preferences preferences;

    /**
     * Resource bundle which delivers column names etc.
     */
    private final RscBundle rsc = RscBundle.getBundle(Rsc.class);

    // constructors

    // overwritten/implemented methods

    /**
     * Gets the rowCount attribute of the PreferencesTableModel object
     *
     * @return The rowCount value
     */
    public int getRowCount() {
        return keys().length;
    }


    /**
     * Gets the columnCount attribute of the PreferencesTableModel object
     *
     * @return The columnCount value
     */
    public int getColumnCount() {
        return 2;
    }


    /**
     * Returns the name of a column from the {@link Rsc resource bundle's}
     * resources {@link Rsc#KEY_COLUMN_NAME} and {@link Rsc#VALUE_COLUMN_NAME}.
     *
     * @param aColumnIndex {@link #KEY_COLUMN} oder {@link #VALUE_COLUMN}.
     * @return Localized name of column.
     */
    public String getColumnName(int aColumnIndex) {
        String rscKey;

        switch (aColumnIndex) {
            case KEY_COLUMN:
                rscKey = Rsc.KEY_COLUMN_NAME;
                break;
            case VALUE_COLUMN:
                rscKey = Rsc.VALUE_COLUMN_NAME;
                break;
            default:
                Log.indexOutOfBounds(this, "getColumnName(int)", "aColumnIndex",
                        aColumnIndex);
                rscKey = Rsc.ERROR;
        }

        return rsc.getString(rscKey);
    }


    /**
     * Returns <code>true</code> for the right column, because the preferences
     * values are editable, but not their keys.
     *
     * @param aRowIndex    Is ignored, there is no difference between the rows.
     * @param aColumnIndex <code>0</code> is not editable, but <code>1</code>
     *                     is.
     * @return <code>aColumnIndex==1</code>.
     */
    public boolean isCellEditable(int aRowIndex, int aColumnIndex) {
        return aColumnIndex == VALUE_COLUMN;
    }


    /**
     * Gets the valueAt attribute of the PreferencesTableModel object
     *
     * @param aRowIndex    Description of Parameter
     * @param aColumnIndex Description of Parameter
     * @return The valueAt value
     */
    public Object getValueAt(int aRowIndex, int aColumnIndex) {
        Object result = null;
        String key = key(aRowIndex);

        switch (aColumnIndex) {
            case KEY_COLUMN:
                result = key;
                break;
            case VALUE_COLUMN:
                String value = preferences.get(key, "<null>");
                try {
                    result = Integer.parseInt(value);
                } catch (NumberFormatException e) {
                }
                if (result == null) {
                    if (Boolean.FALSE.toString().equalsIgnoreCase(value) || Boolean.TRUE.toString().equalsIgnoreCase(value)) {
                        result = Boolean.parseBoolean(value);
                    } else {
                        result = value;
                    }
                }


                break;
            default:
                Log.indexOutOfBounds(this, "getValueAt(int,int)",
                        "aColumnIndex", aColumnIndex);
                result = rsc.getString(Rsc.ERROR);
        }

        return result;
    }


    /**
     * Changes the value of a preference.
     *
     * @param aValue       Must be a string.
     * @param aRowIndex    Determines the key of the preference to be changed.
     * @param aColumnIndex Must be <code>1</code> because that's the values
     *                     column's index.
     */
    public void setValueAt(Object aValue, int aRowIndex, int aColumnIndex) {
        Precondition.argumentEquals("aRowIndex", VALUE_COLUMN, aColumnIndex);
        String key = key(aRowIndex);

        preferences.put(key, aValue.toString());
    }

    // public methods

    /**
     * Assigns a preferences node to this table model.
     *
     * @param somePreferences The preferences object to be displayed or <code>null</code>
     *                        if there is none (e.g. for the tree's root node).
     */
    public void setPreferences(Preferences somePreferences) {
        if (preferences != somePreferences) {
            if (preferences != null) {
                try {
                    preferences.removePreferenceChangeListener(this);
                } catch (IllegalStateException exception) {
                    Log.LOG.logp(Level.FINE,
                            PreferencesTableModel.class.getName(),
                            "setPreferences",
                            exception.getMessage() + " when removing " +
                                    preferences,
                            exception);
                }
            }
            if (somePreferences != null) {
                try {
                    somePreferences.addPreferenceChangeListener(this);
                } catch (IllegalStateException exception) {
                    somePreferences = null;
                    Log.LOG.logp(Level.FINE,
                            PreferencesTableModel.class.getName(),
                            "setPreferences",
                            exception.getMessage() + " when adding " +
                                    somePreferences,
                            exception);
                }
            }
            preferences = somePreferences;
            fireTableDataChanged();
        }
    }

    // implementation of PreferenceChangeListener

    /**
     * Fires a table data changed event.
     *
     * @param anEvent is currently not used, but may be in future.
     */
    public void preferenceChange(PreferenceChangeEvent anEvent) {
        fireTableDataChanged();
    }

    // private methodes

    /**
     * Description of the Method
     *
     * @param aRowIndex Description of Parameter
     * @return Description of the Returned Value
     */
    private String key(int aRowIndex) {
        return keys()[aRowIndex];
    }


    /**
     * Description of the Method
     *
     * @return Description of the Returned Value
     */
    private String[] keys() {
        String[] result;

        try {
            result = (preferences != null) ? preferences.keys() : EMPTY_KEYS;
        } catch (BackingStoreException exception) {
            Log.LOG.logp(Level.FINE, PreferencesTableModel.class.getName(),
                    "keys", exception.getMessage(), exception);
            result = EMPTY_KEYS;
        } catch (IllegalStateException exception) {
            Log.LOG.logp(Level.FINE, PreferencesTableModel.class.getName(),
                    "keys", exception.getMessage(), exception);
            result = EMPTY_KEYS;
        }

        return result;
    }

}
